设计模式(五)工厂模式 & 装饰者模式

  1. 工厂模式
    1. 应用场景
    2. 方案一
    3. 方案二
    4. 方案三
    5. 方案四
    6. 总结
  2. 装饰者模式
    1. 应用场景
    2. 方案一
    3. 方案二
    4. 总结

工厂模式

应用场景

创建不同类型的披萨对象

方案一

直接new出具体类型的对象

public class PizzaStore1 {
    Pizza orderPizza(String type) {
        Pizza pizza;

        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        }
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

方案二

将具体(直接耦合、实现)的、会变化的创建对象部分抽离,包装进一个类,这常被称为“静态工厂”(或叫“简单工厂”)。它的缺点是不能通过继承来改变创建方法的行为。

public class PizzaStore2 {
    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    Pizza orderPizza(String type) {
        Pizza pizza = factory.createPizza(type);    

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

public class SimplePizzaFactory {
    public Pizza createPizza(String type) {
        Pizza pizza = null;
        if (type.equals("cheese")) {
            pizza = new CheesePizza();
        } else if (type.equals("clam")) {
            pizza = new ClamPizza();
        }
        return pizza;
    }
}

通过方案二封装出来的创建披萨简单工厂,将具体的实现解耦出来只依赖接口了,其它分店也可以复用了,修改要创建的类型时也更封闭。

假若PizzaStore开始搞加盟店,不同的加盟店会根据当地的口味调整提供Pizza的种类,在简单工厂中再根据多一个地区标识去判断就会越来越复杂,耦合越来越严重。

方案三

使用“工厂方法”,来监控创建的披萨质量(当然还是可以继承方案二中的简单工厂,提供不同的子类工厂给PizzaStore来创建,这也是工厂方法,但PizzaStore就无法过多地干预创建的过程)

public abstract class PizzaStore3 {
    public Pizza orderPizza(String type) {
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    abstract Pizza createPizza(String type);
}

public class NYPizzaStore3 extends PizzaStore3 {
    Pizza createPizza(String type) {
        if (type.equals("cheese")) {
            return new NYStyleCheesePizza();
        } else if (type.equals("clam")) {
            return new NYStyleClamPizza();
        }
    }
}

public class ChicagoPizzaStore3 extends PizzaStore3 {
    Pizza createPizza(String type) {
        if (type.equals("cheese")) {
            return new ChicagoStyleCheesePizza();
        } else if (type.equals("clam")) {
            return new ChicagoStyleClamPizza();
        }
    }
}

工厂方法(模式)通过让子类决定该创建的对象是什么,达到对象创建的过程封装的目的。

这里还涉及一个涉及原则“反依赖倒置原则”,可以按下面三点方针去遵循:1)变量不可以持有具体类的引用;2)不要让类派生自具体类;3)不要覆盖基类中已实现的方法。

上面的方案二和方案三中的工厂或者工厂方法中,都还是依赖了具体类(即 NY or Chicago 这两个地区种类的披萨),并不符合“反依赖倒置原则”,若未来除cheese和clam外还多了3种披萨,那这两个地区的披萨总和就从4个增至10个

方案四

优化工厂中的依赖关系

public interface PizzaIngredientFactory {
    public Dough createDough();
    public Sauce createSauce();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    public Dough createDough() {
        return new ThinCrustDough();
    }
    public Sauce createSauce() {
        return new MarinaraSauce();
    }
}

public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
    public Dough createDough() {
        return new ThickCrustDough();
    }
    public Sauce createSauce() {
        return new PlumTomatoSauce();
    }
}

public abstract class Pizza {
    String name;
    Dough dough;
    Sauce sauce;

    abstract void prepare();

    //... 其它方法
}

public class CheesePizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

    public CheesePizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    void prepare() {
        //抽象工厂在此起关键作用了
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
    }
    //... 其它方法
}

public class ClamPizza extends Pizza {
    PizzaIngredientFactory ingredientFactory;

    public ClamPizza(PizzaIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    void prepare() {
        dough = ingredientFactory.createDough();
        sauce = ingredientFactory.createSauce();
    }
    //... 其它方法
}

public class NYPizzaStore4 extends PizzaStore3 {
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();

        if (type.equals("cheese")) {
            //(间接)应用抽象工厂(传给产品来使用)
            pizza = new CheesePizza(ingredientFactory);
        } else if (type.equals("clam")) {
            pizza = new ClamPizza(ingredientFactory);
        }
        return pizza;
    }
}

public class ChicagoPizzaStore4 extends PizzaStore3 {
    protected Pizza createPizza(String type) {
        Pizza pizza = null;
        PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();

        if (type.equals("cheese")) {
            pizza = new CheesePizza(ingredientFactory);
        } else if (type.equals("clam")) {
            pizza = new ClamPizza(ingredientFactory);
        }
        return pizza;
    }
}

这个就是“抽象工厂模式”,通过采取对象的组合的方式实现(工厂与产品组合,产品使用不同的工厂创建配件族),现在即使披萨的基本种类增至5个,也不至于要另外为2个不同地区同时新增6(2x3)种地区性披萨,也就是说类的增量只有基本种类的3个新披萨。

同时可以看到,NYPizzaIngredientFactoryChicagoPizzaIngredientFactory里其实都用到了“工厂方法模式”,只是方法不止一个,而是包括了创建这个产品的家族的多个方法。

总结

工厂方法模式是定义出一个创建对象的接口,但由子类决定要实例化的类是哪一个,让类把实例化推迟到子类。抽象工厂模式提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。

装饰者模式

应用场景

计算有n种调料可添加的饮品的价格

方案一

为每种调料都创建一个对应的布尔值,用于在计价时判断是否算上对应调料的价格。

此方案就不贴代码出来了,因为太多的if-else,没什么技术含量。(若不使用该方案,也应该不至于对每个调料创建一个子类来解决if-else多的问题吧)

方案二

将不同的调料定为不同的装饰者,将饮品定为被装饰者,根据需要可包裹多层的装饰(即添加多种调料)

public abstract class Beverage { //当然也可以是接口
    String description = "Unknown beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

public abstract class CondimentDecorator extends Beverage {
    public abstract String getDescription();
}

//基础饮品(第一层被装饰的)
public class Espresso extends Beverage {
    public Espresso() {
        description = "Espresso";
    }

    public double cost() {
        return 1.99;
    }
}

public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "HouseBlend";
    }

    public double cost() {
        return 0.89;
    }
}

//调料(/饮品,第n层被装饰的,也是装饰第n-1层的)
public class Mocha extends CondimentDecorator {
    Beverage beverage;

    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    public double cost() {
        return 0.20 + beverage.cost();
    }
}

装饰者可以在被装饰者的行为前面或/与后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。

可以使用无数个装饰者去包装一个组件,因为装饰者一般对组件的客户是透明的,除非客户依赖的组件是具体类型,当然简单的只包一层也是可以的(不一定要使用上面例子中的嵌套同类的实例对象在类中)。

总结

装饰者模式动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。这是一个符合对扩展开放,对修改关闭原则的模式。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mingfungliu@gmail.com

文章标题:设计模式(五)工厂模式 & 装饰者模式

文章字数:1.8k

本文作者:Mingfung

发布时间:2019-02-12, 11:54:00

最后更新:2019-02-12, 14:34:30

原始链接:http://blog.ifungfay.com/设计模式/设计模式(五)工厂模式 & 装饰者模式/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏

宝贝回家